package roboSoccer;

import java.util.*;

/**
 * RoboTeam represents a team, storing the players on the team, the team's
 * current behavior, and facts that the team knows about the state of the game.
 * When update is called on a RoboTeam, the current state of the game is
 * analyzed and the team's current behavior is evaluated and run.
 * 
 * @author Ben Hopkins
 * 
 */
public class RoboTeam {

	/**
	 * This is the current behavior the team is following.
	 */
	public TeamBehavior teamBehavior;

	/**
	 * An array of RoboPlayer objects representing the robots on this team.
	 */
	public RoboPlayer players[];
	
	/**
	 * Distance from ball indexes for players, from closest to farthest.
	 */
	public int dIndex[];
	
	/**
	 * This team's number.
	 */
	public int teamNumber;

	/**
	 * The goal this team is defending.
	 */
	public Line homeGoal;
	
	/**
	 * The goal this team is trying to score on.
	 */
	public Line targetGoal;
	
	/**
	 * The near quarter line from the home goal.
	 */
	public Line nearQuarter;
	
	/**
	 * The far quarter line from the home goal.
	 */
	public Line farQuarter;
	
	/**
	 * The direction of attack (x) towards the target goal.
	 */
	public int attackDirection;
	
	/**
	 * The number of players on this team currently aiming to go to the ball.
	 */
	public int onBall;

	/**
	 * Current threshold which we judge the team behavior on when deciding to change behaviors.
	 */
	public int changeThreshold;
	
	/**
	 * The list of available team behaviors to change to.
	 */
	List<TeamBehavior> behaviorList;
	
	/**
	 * Initializes players and behaviors for the team. Here behaviors can be added to the behavior
	 * list to make them available for the team to choose.
	 * 
	 * @param teamNumber
	 *            The team number is set in this method.
	 * @param numPlayers
	 *            The number of players is set in this method before they are
	 *            created.
	 */
	public RoboTeam(int teamNumber, int numPlayers) {
		this.teamNumber = teamNumber;
		players = new RoboPlayer[numPlayers];
		dIndex = new int[numPlayers];
		
		for (int i = 0; i < numPlayers; i++)
		{
			players[i] = new RoboPlayer(this, i);
			dIndex[i] = i;
		}

		if(teamNumber == 0)
		{
			homeGoal = GameState.leftGoal;
			targetGoal = GameState.rightGoal;
			nearQuarter = GameState.leftQuarter;
			farQuarter = GameState.rightQuarter;
			attackDirection = 1;
		}
		else
		{
			homeGoal = GameState.rightGoal;
			targetGoal = GameState.leftGoal;
			nearQuarter = GameState.rightQuarter;
			farQuarter = GameState.leftQuarter;
			attackDirection = -1;
		}
		
		// the initial behavior does nothing
		teamBehavior = new TbDefault();
		// change threshold is initialized to a high number so the team will immediately seek a better behavior
		changeThreshold = 100;
		
		// initialize the behavior list with all of the behaviors you want available to the team to choose from.
		behaviorList = new LinkedList<TeamBehavior>();
		behaviorList.add(new TbDefault());
		behaviorList.add(new TbAttack());
	}

	/**
	 * For simulated games, this method give the team's robots control of their simulated players.
	 * @param sim
	 */
	public void setSimulation(SimulationHandler sim)
	{
		for (int i = 0; i < players.length; i++)
			players[i].setSimulatedControl(sim);
	}
	
	/**
	 * For games using NXT robots, this connects the controllers on this team to the NXT bricks.
	 */
	public void setNXT()
	{
		for (int i = 0; i < players.length; i++)
			players[i].setNXTControl();
	}
	
	/**
	 * Takes the first steps in the decision making process for the team. First,
	 * facts about the current state of the game are found and stored in class
	 * variables. These are things like the relative positions of players and
	 * the ball. This information can then be used later. The method then checks
	 * for high-priority commands, such as referee commands or other
	 * user-directed events. These commands can change the team behavior to a
	 * specific behavior if they are executed. Next, the current team behavior's
	 * end method is called, which checks to see if the behavior has finished or
	 * believes that it should terminate, in which case a different behavior
	 * takes over. Finally, the team behavior's update method is called, which
	 * is the guide for the player behaviors.
	 * 
	 * @param game
	 *            The current GameState can tell the team about the positions of
	 *            objects on the field.
	 */
	public void update(GameState game) {
		// reset number of players currently on ball
		onBall = 0;
		
		// make list of players on the team relative to the ball; closest to farthest
		// first store distances
		for(int i = 0; i < players.length; i++)
		{
			players[i].ballDistance = players[i].position.distance(game.ball.position);
			// also note that this is a new turn with no behavior yet assigned
			players[i].behaviorAssigned = false;
		}
		// using gnomesort...
		for(int i = 1; i < dIndex.length;)
		{
			if(i != 0 && players[dIndex[i]].ballDistance < players[dIndex[i-1]].ballDistance)
			{
				int temp = dIndex[i-1];
				dIndex[i-1] = dIndex[i];
				dIndex[i] = temp;
				i--;
			}
			else
				i++;
		}
		
		
		// see if the current behavior should continue.
		if( teamBehavior.behaviorValue(game, this) < changeThreshold )
			assignNewBehavior(game, teamBehavior);

		// call update for our current team behavior.
		teamBehavior.update(game, this);

	}

	/**
	 * This is used to choose a new team behavior.
	 * 
	 * @param game The game state for which the new behavior is being chosen.
	 * @param formerTb The behavior just switched out of that we don't want to switch to again.
	 */
	private void assignNewBehavior(GameState game, TeamBehavior formerTb)
	{
		int bestResult = 0;
		int bestResultIndex = 0;
		for(int i = 0; i < behaviorList.size(); i++)
		{
			// Don't change to the behavior we just changed from,
			//	then test to see if the behavior rates well.
			if(!formerTb.name.equals(behaviorList.get(i).name))
			{
				int tempResult = behaviorList.get(i).behaviorValue(game, this);
				if(bestResult < tempResult)
				{
					bestResult = tempResult;
					bestResultIndex = i;
				}
			}
		}
		changeThreshold = bestResult - 1;
		teamBehavior = behaviorList.get(bestResultIndex).copy();
	
	}
	
}
